#include <QHeaderView>
#include <QSortFilterProxyModel>
#include <QTableView>
#include <QStandardItemModel>
#include "hierarchical-header-view.h"
#include "model-helper.h"
#include "sm-global.h"

QModelIndexList ModelHelper::allIndexes (const QTableView* view)
{
    QModelIndexList indexes;
    for (int i = 0; i < view->model()->rowCount(); i++)
    {
        for (int j = 0; j < view->model()->columnCount(); j++)
        {
            indexes.append(view->model()->index(i, j));
        }
    }
    return indexes;
}

QModelIndexList ModelHelper::visibleIndexes (const QTableView* view)
{
    QModelIndexList indexes;
    for (int i = 0; i < view->model()->rowCount(); i++)
    {
        for (int j = 0; j < view->model()->columnCount(); j++)
        {
            QModelIndex index = view->model()->index(i, j);
            if (!view->isColumnHidden(index.column()))
                indexes.append(index);
        }
    }
    return indexes;
}

QModelIndexList ModelHelper::selectedIndexes (const QAbstractItemView* view)
{
    QModelIndexList indexes = view->selectionModel()->selectedIndexes();
    qSort(indexes);
    return indexes;
}

QModelIndexList ModelHelper::selectedVisibleIndexes (const QTableView* view)
{
    QModelIndexList indexes = view->selectionModel()->selectedIndexes();
    for (int i = 0; i < indexes.size(); i++)
    {
        if (view->isColumnHidden(indexes.at(i).column()))
        {
            indexes.removeAt(i);
            i--;
        }
    }
    qSort(indexes);
    return indexes;
}

QAbstractItemModel* ModelHelper::normalModel (QAbstractItemModel* model)
{
    QSortFilterProxyModel* proxyModel = qobject_cast<QSortFilterProxyModel*>(model);
    return proxyModel ? proxyModel->sourceModel() : model;
}

const QAbstractItemModel* ModelHelper::normalModel (const QAbstractItemModel* model)
{
    const QSortFilterProxyModel* proxyModel = qobject_cast<const QSortFilterProxyModel*>(model);
    return proxyModel ? proxyModel->sourceModel() : model;
}

QModelIndex ModelHelper::normalIndex (const QModelIndex& index)
{
    const QSortFilterProxyModel* proxyModel = qobject_cast<const QSortFilterProxyModel*>(index.model());
    return proxyModel ? proxyModel->mapToSource(index) : index;
}

QModelIndexList ModelHelper::normalIndexes (const QModelIndexList& indexes, bool sort)
{
    if (indexes.isEmpty())
        return indexes;

    const QSortFilterProxyModel* proxyModel = qobject_cast<const QSortFilterProxyModel*>(indexes.at(0).model());
    QModelIndexList normalIndexes;
    if (proxyModel == 0)
    {
        if (sort)
            normalIndexes = indexes;
        else
            return indexes;
    }
    else
    {
        for (int i = 0; i < indexes.size(); i++)
            normalIndexes.append(proxyModel->mapToSource(indexes.at(i)));
    }

    if (sort)
        qSort(normalIndexes);
    return normalIndexes;
}

int ModelHelper::normalSelectedRow (const QAbstractItemView* itemView, int column)
{
    // we assume the selection method is SelectRows and only one row is selected
    Q_ASSERT(itemView->selectionModel()->selectedRows(column).size() == 1);
    return normalIndex(itemView->selectionModel()->selectedRows(column).at(0)).row();
}

QList<int> ModelHelper::normalSelectedRows (const QAbstractItemView* view, int column, bool sort)
{
    // we assume the selection method is SelectRows
    QModelIndexList selectedRows = normalIndexes(view->selectionModel()->selectedRows(column), sort);
    QList<int> normalSelectedRows;
    for (int i = 0; i < selectedRows.size(); i++)
        normalSelectedRows.append(selectedRows.at(i).row());

    return normalSelectedRows;
}

QAbstractItemModel* ModelHelper::findHierarchicalHeaderModel (const QAbstractItemModel* model,
                                                              const Qt::Orientation orientation)
{
    Q_ASSERT(orientation == Qt::Horizontal || orientation == Qt::Vertical);
    QVariant headerModel(model->data(QModelIndex(), (orientation == Qt::Horizontal
                         ? HierarchicalHeaderView::HorizontalHeaderDataRole
                         : HierarchicalHeaderView::VerticalHeaderDataRole)));

    return headerModel.isValid() ? qobject_cast<QAbstractItemModel*>(headerModel.value<QObject*>()) : 0;
}

void ModelHelper::setHierarchicalHeaderModel (QAbstractItemModel* model, const QAbstractItemModel* headerModel,
                                              const Qt::Orientation orientation)
{
    QVariant headerModelVariant;
    headerModelVariant.setValue((QObject*)headerModel);
    model->setData(QModelIndex(), headerModelVariant, (orientation == Qt::Horizontal
                   ? HierarchicalHeaderView::HorizontalHeaderDataRole
                   : HierarchicalHeaderView::VerticalHeaderDataRole));
}

bool ModelHelper::isOnlyOneRowSelected (const QAbstractItemView* view)
{
    QModelIndexList indexes = view->selectionModel()->selectedIndexes();
    return (indexes.first().row() == indexes.last().row());
}

bool ModelHelper::isCurrentIndexSelected (const QAbstractItemView* view)
{
    return view->selectionModel()->selectedIndexes().contains(view->currentIndex());
}

int ModelHelper::rowsInSelection (const QAbstractItemView* view)
{
    QModelIndexList indexes = view->selectionModel()->selectedIndexes();
    if (indexes.isEmpty())
        return 0;

    qSort(indexes);
    int biggestRow = -1;
    int rowCount = 0;

    foreach(const QModelIndex& index, indexes)
    {
        if (index.row() > biggestRow)
        {
            biggestRow = index.row();
            rowCount++;
        }
    }

    return rowCount;
}

void ModelHelper::setLeafCount (const QModelIndex& index, QAbstractItemModel* headerModel)
{
    QVariant leafCount = index.data(SmGlobal::LeafCountRole);
    if (leafCount.isValid() == false || leafCount.toInt() == 0)
    {
        if (headerModel->columnCount(index) == 0)    // if it's a leaf
        {
            headerModel->setData(index, 1, SmGlobal::LeafCountRole);
        }
        else
        {
            int leafCount = 0;
            for (int i = 0; i < headerModel->columnCount(index); i++)
            {
                setLeafCount(headerModel->index(0, i, index), headerModel);
                leafCount += headerModel->index(0, i, index).data(SmGlobal::LeafCountRole).toInt();
            }
            headerModel->setData(index, leafCount, SmGlobal::LeafCountRole);
        }
    }
}

void ModelHelper::setLeafCount (QStandardItem* item)
{
    QVariant leafCount = item->data(SmGlobal::LeafCountRole);
    if (leafCount.isValid() == false || leafCount.toInt() == 0)
    {
        if (item->columnCount() == 0)    // if it's a leaf
        {
            item->setData(1, SmGlobal::LeafCountRole);
        }
        else
        {
            int leafCount = 0;
            for (int i = 0; i < item->columnCount(); i++)
            {
                setLeafCount(item->child(0, i));
                leafCount += item->child(0, i)->data(SmGlobal::LeafCountRole).toInt();
            }
            item->setData(leafCount, SmGlobal::LeafCountRole);
        }
    }
}

void ModelHelper::setVisibleLeafCount (QAbstractItemModel* headerModel, const QTableView* tableView,
                                       const QModelIndex& index, int section)
{
    // assume the leaf count is already set
    Q_ASSERT(index.isValid() ? (index.data(SmGlobal::LeafCountRole).toInt() != 0) : true);
    if (section == -1)
    {
        section = 0;
        if (index.isValid())
        {
            for (int i = 0; i < index.column(); i++)
                section += headerModel->index(0, i).data(SmGlobal::LeafCountRole).toInt();
        }
        else
        {
            // set visible leaf count for all columns
            int leafCount = 0;
            for (int i = 0; i < headerModel->columnCount(index); i++)
            {
                setVisibleLeafCount(headerModel, tableView, headerModel->index(0, i, index), section+leafCount);
                leafCount += headerModel->index(0, i, index).data(SmGlobal::LeafCountRole).toInt();
            }
            return;
        }
    }

    if (headerModel->columnCount(index) == 0)
    {
        if (tableView->isColumnHidden(section))
            headerModel->setData(index, 0, SmGlobal::VisibleLeafCountRole);
        else
            headerModel->setData(index, 1, SmGlobal::VisibleLeafCountRole);
    }
    else
    {
        int visibleLeafCount = 0;
        int leafCount = 0;
        for (int i = 0; i < headerModel->columnCount(index); i++)
        {
            setVisibleLeafCount(headerModel, tableView, headerModel->index(0, i, index), section+leafCount);
            visibleLeafCount += headerModel->index(0, i, index).data(SmGlobal::VisibleLeafCountRole).toInt();
            leafCount += headerModel->index(0, i, index).data(SmGlobal::LeafCountRole).toInt();
        }
        headerModel->setData(index, visibleLeafCount, SmGlobal::VisibleLeafCountRole);
    }
}

int ModelHelper::bottomSectionCount (const QAbstractItemModel* headerModel)
{
    if (headerModel->rowCount() == 0)
        return 0;

    // we assume the leaves are already counted
    Q_ASSERT(headerModel->index(0, 0).data(SmGlobal::LeafCountRole).isValid());
    int sectionCount = 0;
    for (int i = 0; i < headerModel->columnCount(); i++)
        sectionCount += headerModel->index(0, i).data(SmGlobal::LeafCountRole).toInt();
    return sectionCount;
}

int ModelHelper::bottomVisibleSectionCount (const QAbstractItemModel* headerModel)
{
    if (headerModel->rowCount() == 0)
        return 0;

    // we assume the visible leaves are already counted
    int sectionCount = 0;
    for (int i = 0; i < headerModel->columnCount(); i++)
    {
        Q_ASSERT(headerModel->index(0, i).data(SmGlobal::VisibleLeafCountRole).isValid());
        sectionCount += headerModel->index(0, i).data(SmGlobal::VisibleLeafCountRole).toInt();
    }
    return sectionCount;
}


// a helper function to print out contents of a table model
// it's compiled only in debug mode
#include <QDebug>
void ModelHelper::printModelContent (const QAbstractItemModel* model)
{
#ifdef QT_NO_DEBUG
    qDebug("WARNING - USING PRINT MODEL CONTENT IN RELEASE MODE");
#endif
    qDebug() << "printing model" << model << "rowCount" << model->rowCount() << "columnCount" << model->columnCount();
    QString header;
    for (int k = 0; k < model->columnCount(); k++)
    {
        header.append(model->headerData(k, Qt::Horizontal).toString());
        if (k != model->columnCount()-1)
            header.append(", ");
    }
    qDebug() << "header =" << header;

    for (int i = 0; i < model->rowCount(); i++)
    {
        QString row;
        for (int j = 0; j < model->columnCount(); j++)
        {
            row.append(model->index(i, j).data().toString());
            if (j != model->columnCount()-1)
                row.append(", ");
        }
        qDebug() << "row =" << i << ": " << row;
    }
}

